์ž‘์„ฑ: 2026-03-04 04:03:38์ˆ˜์ •: 2026-03-04 04:03:38

Spring Boot ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑํ•˜๊ธฐ: JUnit 5์™€ Mockito ํ™œ์šฉ ๊ฐ€์ด๋“œ

๊ฒฌ๊ณ ํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ•ต์‹ฌ์€ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ "์ฝ”๋“œ๊ฐ€ ๋Œ์•„๊ฐ€๋Š” ๊ฒƒ"์„ ๋„˜์–ด, ์˜๋„ํ•œ ๋Œ€๋กœ ๋™์ž‘ํ•จ์„ ๊ฒ€์ฆํ•˜๊ณ  ๋ฆฌํŒฉํ† ๋ง์˜ ์•ˆ์ „๋ง์„ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค. 2026๋…„ ๊ธฐ์ค€ ๊ฐ€์žฅ ๋งŽ์ด ์“ฐ์ด๋Š” JUnit 5์™€ Mockito๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ์‚ดํŽด๋ด…๋‹ˆ๋‹ค.


1. ํ…Œ์ŠคํŠธ์˜ ์ข…๋ฅ˜

  • ๋‹จ์œ„ ํ…Œ์ŠคํŠธ(Unit Test): ํ•˜๋‚˜์˜ ๋ฉ”์†Œ๋“œ๋‚˜ ํด๋ž˜์Šค๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค. ์™ธ๋ถ€ ์˜์กด์„ฑ(DB ๋“ฑ)์€ ๊ฐ€์งœ ๊ฐ์ฒด(Mock)๋กœ ๋Œ€์ฒดํ•˜์—ฌ ๋งค์šฐ ๋น ๋ฅด๊ฒŒ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
  • ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ(Integration Test): ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‹ค์ œ๋กœ ๋„์›Œ ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ ๊ฐ„์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค. ์‹ค์ œ DB ์—ฐ๋™๊นŒ์ง€ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.

2. ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ž‘์„ฑํ•˜๊ธฐ (Mockito ํ™œ์šฉ)

์„œ๋น„์Šค ๋ ˆ์ด์–ด๋ฅผ ํ…Œ์ŠคํŠธํ•  ๋•Œ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ ‘๊ทผ ๊ฐ์ฒด(Repository)๋ฅผ ๊ฐ€์งœ๋กœ ๋งŒ๋“ค์–ด ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—๋งŒ ์ง‘์ค‘ํ•ฉ๋‹ˆ๋‹ค.

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
 
    @Mock
    private UserRepository userRepository;
 
    @InjectMocks
    private UserService userService;
 
    @Test
    @DisplayName("ํšŒ์› ๊ฐ€์ž… ์„ฑ๊ณต ํ…Œ์ŠคํŠธ")
    void join_Success() {
        // given (์ค€๋น„)
        User user = new User("ํ™๊ธธ๋™", "[email protected]");
        given(userRepository.save(any())).willReturn(user);
 
        // when (์‹คํ–‰)
        Long savedId = userService.join(user);
 
        // then (๊ฒ€์ฆ)
        assertThat(savedId).isNotNull();
        verify(userRepository, times(1)).save(any());
    }
}

3. ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ž‘์„ฑํ•˜๊ธฐ (@SpringBootTest)

API ์ปจํŠธ๋กค๋Ÿฌ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊นŒ์ง€ ์ „์ฒด ํ๋ฆ„์„ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค.

@SpringBootTest
@AutoConfigureMockMvc
@Transactional // ํ…Œ์ŠคํŠธ ํ›„ ๋ฐ์ดํ„ฐ ๋กค๋ฐฑ
class UserApiTest {
 
    @Autowired
    private MockMvc mockMvc;
 
    @Test
    @DisplayName("API๋ฅผ ํ†ตํ•œ ์‚ฌ์šฉ์ž ์กฐํšŒ ํ…Œ์ŠคํŠธ")
    void getUser_ApiTest() throws Exception {
        mockMvc.perform(get("/api/users/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name").value("ํ™๊ธธ๋™"));
    }
}

4. ์ข‹์€ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์œ„ํ•œ ์›์น™ (F.I.R.S.T)

  1. Fast (๋น ๋ฅด๊ฒŒ): ํ…Œ์ŠคํŠธ๋Š” ์ž์ฃผ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋ฏ€๋กœ ๋นจ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  2. Independent (๋…๋ฆฝ์ ์œผ๋กœ): ๊ฐ ํ…Œ์ŠคํŠธ๋Š” ์„œ๋กœ ์˜์กดํ•˜์ง€ ์•Š๊ณ  ๋‹จ๋…์œผ๋กœ ์‹คํ–‰ ๊ฐ€๋Šฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  3. Repeatable (๋ฐ˜๋ณต ๊ฐ€๋Šฅํ•˜๊ฒŒ): ์–ด๋–ค ํ™˜๊ฒฝ์—์„œ๋„ ๊ฒฐ๊ณผ๊ฐ€ ํ•ญ์ƒ ๊ฐ™์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  4. Self-Validating (์ž๊ฐ€ ๊ฒ€์ฆ): ์„ฑ๊ณต/์‹คํŒจ ์—ฌ๋ถ€๋ฅผ ์ฝ”๋“œ๊ฐ€ ์ง์ ‘ ์•Œ๋ ค์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  5. Timely (์ ์‹œ์—): ์‹ค์ œ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์ง์ „์ด๋‚˜ ์งํ›„์— ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

5. ๊ฒฐ๋ก 

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ์ž‘์„ฑํ•  ๋•Œ๋Š” ๋ฒˆ๊ฑฐ๋กญ์ง€๋งŒ, ์žฅ๊ธฐ์ ์œผ๋กœ๋Š” ๋ฒ„๊ทธ ์ˆ˜์ • ๋น„์šฉ์„ ํš๊ธฐ์ ์œผ๋กœ ๋‚ฎ์ถฐ์ค๋‹ˆ๋‹ค. ํŠนํžˆ **TDD(ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ)**๋ฅผ ์‹ค์ฒœํ•ด๋ณด๋ฉฐ ์ฝ”๋“œ์˜ ํ’ˆ์งˆ์„ ๋†’์—ฌ๋ณด์„ธ์š”!